<template>
  <a-row type="flex" style="height: 100%; width: 100%">
    <a-col flex="300px" style="height: 100%">
      <a-card
        style="width: 100%; height: 100%"
        title="控件"
        size="small"
        :body-style="{ padding: '5px', height: '100%' }"
      >
        <s-scroll>
          <draggable
            v-model="components"
            :group="{ name: 'dragContext', pull: 'clone', put: false }"
            animation="300"
            :sort="false"
            :clone="cloneData"
            :move="move"
            item-key="id"
          >
            <template #item="{ element }">
              <component-definition
                :on-add="onItemAdd"
                :name="element.label"
                :icon="element.icon"
                :template-data="templates[element.value]"
              />
            </template>
          </draggable>
        </s-scroll>
      </a-card>
    </a-col>
    <a-col flex="auto" style="height: 100%; overflow: hidden; flex-basis: 0">
      <a-row>
        <a-tooltip>
          <template #title> 勾选此项时，可以拖拽的区域会有边框效果 </template>
          <a-checkbox
            :default-checked="true"
            @change="
              (e) => {
                dragClass = e.target.checked ? 'dragContent' : ''
              }
            "
            >拖拽区域边框</a-checkbox
          >
        </a-tooltip>
        <a-tooltip>
          <template #title>
            勾选此项时，如果组件不满足显示表达式，则组件不会在此面板中显示
          </template>
          <a-checkbox
            :default-checked="true"
            @change="
              (e) => {
                logicControl = e.target.checked ? true : false
              }
            "
            >逻辑控制</a-checkbox
          >
        </a-tooltip>
        <a-button-group size="small">
          <a-tooltip>
            <template #title> 效果预览，将会与实际效果一致 </template>
            <a-button type="primary" :ghost="true" @click="preview">
              <s-icon type="eye-outlined" />预览
            </a-button>
          </a-tooltip>
          <a-tooltip>
            <template #title> 显示组件定义代码，并且可以手动进行更改 </template>
            <a-button type="primary" :ghost="true" @click="debug"
              ><s-icon type="database-outlined" />组件定义</a-button
            >
          </a-tooltip>
          <a-button type="primary" :ghost="true" @click="itemPageDialog"
            ><s-icon type="code-outlined" />代码</a-button
          >
        </a-button-group>
      </a-row>
      <a-row style="height: calc(100% - 30px); width: 100%; overflow: hidden">
        <vue-scroll
          :ops="{
            vuescroll: {},
            scrollPanel: {
              scrollingX: true,
              scrollingY: true
            },
            rail: {
              keepShow: true
            },
            bar: {
              hoverStyle: true,
              onlyShowBarOnScroll: false,
              background: '#81D3F8',
              opacity: 0.5
            }
          }"
          style="width: 100%; height: 100%"
        >
          <div
            class="ant-modal-content"
            :style="{ width: properties.width, marginTop: '30px', marginLeft: '20px' }"
          >
            <div class="ant-modal-header">
              <div class="ant-modal-title">表单</div>
            </div>
            <div class="ant-modal-body">
              <div :style="{ height: properties.height }">
                <s-scroll>
                  <s-form ref="form" style="width: 100%" :value="formValue">
                    <drag-form
                      ref="dragPanel"
                      :items="items"
                      :drag-class="dragClass"
                      :item-choose="choose"
                      drag-group-name="dragContext"
                      :form="formValue"
                      :select-id="selectId"
                      :logic-control="logicControl"
                      @select="select"
                      @remove="removeSelectItem"
                    />
                  </s-form>
                </s-scroll>
              </div>
            </div>
            <div class="ant-modal-footer">
              <div>
                <button type="button" class="ant-btn"><span>取 消</span></button>
                <button type="button" class="ant-btn ant-btn-primary"><span>确定</span></button>
              </div>
            </div>
          </div>
        </vue-scroll>
      </a-row>
    </a-col>
    <a-col flex="300px" style="height: 100%">
      <template v-if="editItem != null">
        <a-card
          style="width: 100%; height: 100%"
          title="属性"
          size="small"
          :body-style="{ padding: '5px', height: '100%' }"
        >
          <template #extra>
            <s-icon
              type="close-outlined"
              @click="
                () => {
                  selectId = null
                  editItem = null
                }
              "
            />
          </template>
          <div style="height: calc(100% - 70px); padding: 10px">
            <property-panel
              ref="propertyPanel"
              v-model="editItem"
              :code="code"
              :on-save="onSave"
              :key-options="keyOptions"
              class="property-panel"
            />
          </div>
          <div class="footer">
            <button
              type="button"
              size="small"
              class="ant-btn ant-btn-primary"
              style="float: right; margin-right: 10px"
              @click="saveProperty"
            >
              <span>应 用</span>
            </button>
          </div>
        </a-card>
      </template>
      <template v-if="editItem == null">
        <a-card
          style="width: 100%; height: 100%"
          title="属性"
          size="small"
          :body-style="{ padding: '5px', height: '100%' }"
        >
          <div style="height: calc(100% - 70px); padding: 10px">
            <form-property-panel
              ref="formPropertyPanel"
              :value="properties"
              class="property-panel"
            />
          </div>
          <div class="footer">
            <button
              type="button"
              class="ant-btn ant-btn-primary"
              style="float: right; margin-right: 10px"
              @click="saveFormProperty"
            >
              <span>应 用</span>
            </button>
          </div>
        </a-card>
      </template>
      <display ref="display"></display>
      <preview ref="preview" :properties="properties" :items="items" />
      <json-display ref="jsonDisplay" :save="itemSave" />
    </a-col>
  </a-row>
</template>
<style scoped type="less">
/*被拖拽对象的样式*/
.ant-modal-body:deep(.form-design-item-checked) {
  border-radius: 3px;
  background-color: #81d3f8;
}
.ant-modal-body:deep(.dragContent) {
  box-shadow: inset 0 1px #f0f0f0, inset 0 -1px #f0f0f0, inset -1px 0 #f0f0f0, inset 1px 0 #f0f0f0;
}
.property-panel:deep(.ant-tabs-content) {
  height: 100%;
  width: 100%;
}
.property-panel:deep(.ant-tabs-content-holder) {
  height: 100%;
  width: 100%;
  flex-basis: 0;
}
.property-panel:deep(.property-content) {
  height: 100%;
  width: 100%;
}
</style>
<script>
/* eslint-disable no-eval */

import ComponentDefinition from './components/ComponentDefinition.vue'
import { templates, components } from './components/templateData.js'
import PropertyPanel from './properties/PropertyPanel.vue'
import FormPropertyPanel from './properties/FormPropertyPanel.vue'
import renderComponentAttrs from './render.js'
import Preview from './Preview/index.vue'
import DragForm from './dragForm/index.vue'
import draggable from 'vuedraggable'
import { v4 } from 'uuid'
import Display from './display/index.vue'
import JsonDisplay from './display/json/index.vue'
export default {
  name: 'DynamicFormDesigner',
  components: {
    draggable,
    ComponentDefinition,
    PropertyPanel,
    DragForm,
    Display,
    FormPropertyPanel,
    Preview,
    JsonDisplay
  },
  props: {
    keyOptions: {
      required: false,
      type: Array,
      default: () => []
    },
    value: {
      required: false,
      type: Object,
      default: () => {}
    },
    code: {
      type: Boolean,
      required: false,
      default: false
    },
    formDefine: {
      required: false,
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
      formValue: {},
      dragClass: 'dragContent',
      checkClass: '',
      templates: templates,
      components: components,
      selectId: undefined,
      editItem: null,
      logicControl: true,
      index: 1,
      // 定义要被拖拽对象的数组
      items: [],
      properties: {
        height: '400px',
        width: '800px'
      }
    }
  },
  created() {
    if (this.formDefine) {
      this.init({
        data: this.formDefine.data,
        index: this.formDefine.index,
        properties: this.formDefine.properties
      })
    }
  },
  methods: {
    move(e) {},
    getData() {
      const result = {
        htmls: {
          common: this.$refs.dragPanel.buildHtml(false, '          ', (that) => {
            return false
          }),
          disabled: this.$refs.dragPanel.buildHtml(true, '          ', (that) => {
            return false
          })
        },
        properties: this.properties,
        data: this.$refs.dragPanel.getData(null, (that) => {
          return false
        }),
        index: this.index
      }
      return result
    },
    init(data) {
      this.items = data.data
      this.index = data.index
      if (data.properties) {
        this.properties = data.properties
      }
    },
    test() {
      return eval('(test)=>{return test=="dragContent"}')(this.dragClass)
    },
    debug() {
      this.$refs.jsonDisplay.open(
        this.$refs.dragPanel.getData(null, (that) => {
          return false
        })
      )
    },
    preview() {
      this.items = this.$refs.dragPanel.getData(null, (that) => {
        return false
      })
      this.$refs.preview.open()
    },
    itemPageDialog() {
      this.$refs.display.open(
        this.$refs.dragPanel.buildHtml(false, '    ', (that) => {
          return false
        })
      )
    },
    select(id) {
      this.selectId = null
      this.$nextTick(() => {
        const item = this.$refs.dragPanel.findItemById(id)
        if (item) {
          this.editItem = JSON.parse(JSON.stringify(item))
          this.selectId = id.toString()
        }
      })
    },
    choose(ele) {
      if (ele) {
        const item = this.$refs.dragPanel.findItemById(ele.item.id)
        if (item) {
          this.editItem = JSON.parse(JSON.stringify(item))
          this.selectId = ele.item.id
        }
      } else {
        this.selectId = null
      }
    },
    onItemAdd(item) {
      const msg = this.validate(item)
      if (!msg) {
        this.renderItem(item)
        if (item.properties.key) {
          this.formValue[item.properties.key] = undefined
        }
        renderComponentAttrs(item)
        this.index = this.index + 1
        this.$refs.dragPanel.addItem(item)
      } else {
        this.$message.info(msg)
      }
    },
    validate(item) {
      if (item.properties.key) {
        const finded = this.$refs.dragPanel.findItemByKey(item.properties.key)
        if (finded && finded.properties.key != item.properties.key) {
          return item.properties.key + '已存在'
        }
      }
    },
    renderItem(item) {
      Object.keys(item).forEach((key) => {
        const value = item[key]
        if (value) {
          if (Array.isArray(value)) {
            value.forEach((v) => {
              this.renderItem(v)
            })
          } else if (typeof value == 'string') {
            item[key] = value.replace('{i}', this.index + '')
          } else if (Object.prototype.toString.call(value) === '[object Object]') {
            this.renderItem(value)
          }
        }
      })
    },
    saveProperty() {
      this.$refs.propertyPanel.save()
    },
    saveFormProperty() {
      this.$refs.formPropertyPanel.save((item) => {
        this.properties = item
      })
    },
    onSave(val) {
      const msg = this.validate(val)
      if (val.properties.key) {
        this.formValue[val.properties.key] = undefined
      }
      if (!msg) {
        const id = val.id
        this.updateById(id, val)
        this.$message.info('成功')
      } else {
        this.$message.info(msg)
      }
    },
    updateById(id, val) {
      const item = this.$refs.dragPanel.findItemById(id)
      if (item) {
        renderComponentAttrs(val)
        item.children = val.children
        item.type = val.type
        item.attrs = val.attrs
        item.extendAttrs = val.extendAttrs
        item.properties = val.properties
        item.children = val.children
      }
    },
    removeSelectItem() {
      this.$refs.dragPanel.removeItemById(this.selectId)
      this.selectId = null
      this.editItem = null
    },
    cloneData(a) {
      const item = JSON.parse(JSON.stringify(templates[a.value]))
      item.id = v4()
      this.renderItem(item)
      if (item.properties.key) {
        this.formValue[item.properties.key] = undefined
      }
      renderComponentAttrs(item)
      this.index = this.index + 1
      return item
    },
    itemSave(data) {
      this.items = data
    }
  }
}
</script>
